/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.uml_generator.manager.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.genia.toolbox.basics.exception.technical.TechnicalIOException;
import com.genia.toolbox.uml_generator.Constants;
import com.genia.toolbox.uml_generator.manager.AssociationManager;
import com.genia.toolbox.uml_generator.model.AbstractClassModel;
import com.genia.toolbox.uml_generator.model.AssociationCardinality;
import com.genia.toolbox.uml_generator.model.AssociationModel;
import com.genia.toolbox.uml_generator.model.AttributeModel;
import com.genia.toolbox.uml_generator.model.OperationModel;
import com.genia.toolbox.uml_generator.model.impl.AttributeModelImpl;
import com.genia.toolbox.uml_generator.model.impl.OperationModelImpl;

/**
 * implementation of {@link AssociationManager}.
 */
public class AssociationManagerImpl
    extends AbstractManager
    implements AssociationManager
{

  /**
   * get all methods related to an association.
   * 
   * @param association
   *          the association to print
   * @return the methods related to the association
   * @throws TechnicalIOException
   *           when an error occurred
   */
  public Collection<OperationModel> getAccessorMethodForAssociation(AssociationModel association)
      throws TechnicalIOException
  {
    List<OperationModel> res = new ArrayList<OperationModel>();
    res.add(getSimpleGetter(association));
    res.addAll(getSetterOperationForSets(association));
    if (association.getQualifier() != null) {
      OperationModel removeOperation = getRemoveOperationTemplate(association);
      OperationModel removeByKeyOperation = getRemoveByKeyOperationTemplate(association);
      String parameterName = removeByKeyOperation.getParameterModels().get(0).getName();
      StringBuilder body = new StringBuilder();
      OperationModel internalAssociationGetter = getAttributeManager().getGetter(getInternalAttributeForAssociation(association));
      body.append("if(").append(parameterName).append(" == null) {\nreturn;\n}\n");
      body.append(removeOperation.getName()).append("(").append(internalAssociationGetter.getName()).append("().get(").append(parameterName).append("));\n");
      removeByKeyOperation.setBody(body.toString());
      res.add(removeByKeyOperation);
    }
    if (association.getOtherEndAssociation() == null) {
      res.addAll(getAccessorMethodForOneWayAssociation(association));
    }
    else {
      res.addAll(getAccessorMethodForTwoWayAssociation(association));
    }
    return res;
  }



  /**
   * returns all methods related to the associations given in parameter.
   * 
   * @param associations
   *          the associations to work with
   * @return all methods related to the associations given in parameter
   * @throws TechnicalIOException
   *           when an error occurred
   */
  public Collection<OperationModel> getAccessorMethodForAssociations(Collection<AssociationModel> associations)
      throws TechnicalIOException
  {
    List<OperationModel> res = new ArrayList<OperationModel>();
    for (AssociationModel association : associations) {
      res.addAll(getAccessorMethodForAssociation(association));
    }
    return res;
  }



  /**
   * returns the internal attribute associated to an association.
   * 
   * @param association
   *          the association
   * @return the attribute associated to the association
   * @throws TechnicalIOException
   *           when an I/O error occurred
   * @see com.genia.toolbox.uml_generator.manager.AssociationManager#getInternalAttributeForAssociation(com.genia.toolbox.uml_generator.model.AssociationModel)
   */
  public AttributeModel getInternalAttributeForAssociation(AssociationModel association)
      throws TechnicalIOException
  {
    return getModelDecorator().getInternalObject(getAttributeForAssociation(association));
  }



  /**
   * returns wether this end of an association is the delegating one, or not.
   * 
   * @param association
   *          the association to inspect
   * @return <code>false</code> if this end of the association is the
   *         delegating one
   */
  public boolean hasPriority(AssociationModel association)
  {
    if (association.getOtherEndAssociation() == null) {
      return true;
    }
    if (association.getOtherCardinality() == association.getCurrentCardinality()) {
      return association.getAssociatedType().getQualifiedName().compareTo(association.getOtherEndAssociation().getAssociatedType().getQualifiedName()) > 0;
    }
    else {
      return (association.getOtherCardinality() == AssociationCardinality.ONE);
    }
  }



  /**
   * return the {@link Set} of {@link AssociationModel} that are new (not in the
   * super class) to the {@link AbstractClassModel} given in parameter.
   * 
   * @param classModel
   *          the {@link AbstractClassModel} to inspect.
   * @return the {@link Set} of {@link AssociationModel} that are new to the
   *         {@link AbstractClassModel} given in parameter
   * @see com.genia.toolbox.uml_generator.manager.AssociationManager#newAssociations(com.genia.toolbox.uml_generator.model.AbstractClassModel)
   */
  public Set<AssociationModel> newAssociations(AbstractClassModel classModel)
  {
    Set<AssociationModel> res = new HashSet<AssociationModel>(classModel.getAssociations());
    if (classModel.getParent() != null) {
      res.removeAll(classModel.getParent().getAssociations());
    }
    return res;
  }



  /**
   * return the {@link Set} of {@link AssociationModel} that are new (not either
   * in the super class or the implemented interfaces) to the
   * {@link AbstractClassModel} given in parameter.
   * 
   * @param classModel
   *          the {@link AbstractClassModel} to inspect.
   * @return the {@link Set} of {@link AssociationModel} that are new to the
   *         {@link AbstractClassModel} given in parameter
   * @see com.genia.toolbox.uml_generator.manager.AssociationManager#newAssociationsForInterface(com.genia.toolbox.uml_generator.model.AbstractClassModel)
   */
  public Set<AssociationModel> newAssociationsForInterface(AbstractClassModel classModel)
  {
    Set<AssociationModel> res = newAssociations(classModel);
    for (AbstractClassModel interfaceModel : classModel.getImplementsModel()) {
      res.removeAll(interfaceModel.getAssociations());
    }
    return res;
  }



  /**
   * get all methods related to the an one way association.
   * 
   * @param association
   *          the association to consider
   * @return all methods related to the association
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private Collection<OperationModel> getAccessorMethodForOneWayAssociation(AssociationModel association)
      throws TechnicalIOException
  {
    List<OperationModel> res = new ArrayList<OperationModel>();
    if (association.getOtherCardinality() == AssociationCardinality.MANY) {
      StringBuilder body;
      String parameterName = null;

      OperationModel addOperation = getAddOperationTemplate(association);
      parameterName = addOperation.getParameterModels().get(0).getName();
      body = new StringBuilder();
      body.append("if(").append(parameterName).append(" == null) {\nreturn;\n}\n");
      body.append(getEffectiveAdd(association, parameterName)).append(";");
      addOperation.setBody(body.toString());
      res.add(addOperation);

      OperationModel removeOperation = getRemoveOperationTemplate(association);
      parameterName = removeOperation.getParameterModels().get(0).getName();
      body = new StringBuilder();
      body.append("if(").append(parameterName).append(" == null) {\nreturn;\n}\n");
      body.append(getEffectiveRemove(association, parameterName)).append(";");
      removeOperation.setBody(body.toString());
      res.add(removeOperation);

    }
    else {
      OperationModel setterOperation = getAttributeManager().getSetter(getAttributeForAssociation(association));
      OperationModel internalSetterOperation = getAttributeManager().getSetter(getInternalAttributeForAssociation(association));
      setterOperation.setBody("this." + internalSetterOperation.getName() + "(" + setterOperation.getParameterModels().get(0).getName() + ");");
      res.add(setterOperation);
    }
    return res;
  }



  /**
   * returns all methods related to the a two way association.
   * 
   * @param association
   *          the association to consider
   * @return all methods related to the association
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private Collection<OperationModel> getAccessorMethodForTwoWayAssociation(AssociationModel association)
      throws TechnicalIOException
  {
    if (hasPriority(association)) {
      return getPriorityAccessorMethodForTwoWayAssociation(association);
    }
    else {
      return getNonPriorityAccessorMethodForTwoWayAssociation(association);
    }
  }



  /**
   * get a template operation for an Add operation for an association.
   * 
   * @param association
   *          the association to consider
   * @return a template operation for an Add operation for an association
   */
  private OperationModel getAddOperationTemplate(AssociationModel association)
  {
    OperationModel operation = new OperationModelImpl();
    operation.setName("add" + getNameManager().getUpperCaseFirstLetterName(association));
    operation.setReturnType(getAttributeManager().getVoidReturnType());

    AttributeModel parameter = new AttributeModelImpl();
    parameter.setName(association.getName());
    parameter.setType(association.getAssociatedType());
    parameter.setComment("the " + association.getName() + " to add.");
    parameter.setUmlId(association.getUmlId() + "addParameter");
    operation.addParameterModels(parameter);

    operation.setComment("add a new " + association.getName() + ".");
    operation.setUmlId(association.getUmlId() + "add");
    return operation;
  }



  /**
   * returns the {@link OperationModel} representing the setter operation of a
   * ?-to-MANY relation.
   * 
   * @param association
   *          the association to generate the setter for
   * @return the {@link OperationModel} representing the setter operation of a
   *         ?-to-MANY relation
   * @throws TechnicalIOException
   *           if an error occured
   */
  private List<OperationModel> getSetterOperationForSets(AssociationModel association)
      throws TechnicalIOException
  {
    if (association.getQualifier() != null || association.getOtherCardinality() != AssociationCardinality.MANY) {
      return Arrays.asList();
    }

    OperationModel operation = getAttributeManager().getSetter(getModelDecorator().getPluralObject(getAttributeForAssociation(association)));
    final AttributeModel attribute = operation.getParameterModels().get(0);
    AttributeModel returnType = new AttributeModelImpl();
    returnType.setComment("the objects that are not in the property anymore.");
    returnType.setName(operation.getReturnType().getName());
    returnType.setType(attribute.getType());
    operation.setReturnType(returnType);

    StringBuilder body = new StringBuilder();
    body.append(operation.getReturnType().getType().getQualifiedName()).append(" res = ").append(operation.getReturnType().getInitialValue()).append(";");
    body.append("res.addAll(").append(getAttributeManager().getGetter(getInternalAttributeForAssociation(association)).getName()).append("());");
    body.append("for(").append(association.getAssociatedType().getQualifiedName()).append(" ").append(association.getName()).append(":res){");
    body.append("if(!").append(attribute.getName()).append(".contains(").append(association.getName()).append(")) {");
    body.append(getRemoveOperationTemplate(association).getName()).append("(").append(association.getName()).append(");");
    body.append("}");
    body.append("}");
    body.append("for(").append(association.getAssociatedType().getQualifiedName()).append(" ").append(association.getName()).append(":").append(attribute.getName()).append("){");
    body.append(getAddOperationTemplate(association).getName()).append("(").append(association.getName()).append(");");
    body.append("res.remove(").append(association.getName()).append(");");
    body.append("}");

    body.append("return res;");

    operation.setBody(body.toString());

    return Arrays.asList(operation);
  }



  /**
   * returns the attribute associated to an association.
   * 
   * @param association
   *          the association
   * @return the attribute associated to the association
   * @throws TechnicalIOException
   *           when an I/O error occurred
   * @see com.genia.toolbox.uml_generator.manager.AssociationManager#getInternalAttributeForAssociation(com.genia.toolbox.uml_generator.model.AssociationModel)
   */
  private AttributeModel getAttributeForAssociation(AssociationModel association)
      throws TechnicalIOException
  {
    AttributeModel attribute = new AttributeModelImpl();
    attribute.setMappable(false);
    attribute.setComment(association.getComment());
    attribute.setUmlId(association.getUmlId() + "Attribute");
    attribute.setName(association.getName());
    attribute.setType(getModelInspector().getTypeForAssociation(association));
    return attribute;
  }



  /**
   * returns the command to use to add an element to an association.
   * 
   * @param association
   *          the association to consider
   * @param variable
   *          the variable containing the element to add
   * @return the command to use to add an element to an association
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private String getEffectiveAdd(AssociationModel association, String variable)
      throws TechnicalIOException
  {
    String res = null;
    switch (association.getOtherCardinality()) {
      case ONE:
        res = getAttributeManager().getSetter(getInternalAttributeForAssociation(association)).getName() + "(" + variable + ")";
        break;
      case MANY:
        if (association.getQualifier() == null) {
          res = getAttributeManager().getGetter(getInternalAttributeForAssociation(association)).getName() + "().add(" + variable + ")";
        }
        else {
          // TODO insure to get the right getter
          res = getAttributeManager().getGetter(getInternalAttributeForAssociation(association)).getName() + "().put(" + variable + ".get"
              + getNameManager().getUpperCaseFirstLetterName(association.getQualifier()) + "(), " + variable + ")";
        }
    }
    return res;
  }



  /**
   * returns the command to use to verify that an element is contained in an
   * association.
   * 
   * @param association
   *          the association to consider
   * @param variable
   *          the variable containing the element to check
   * @return the command to use to verify that an element is contained in an
   *         association
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private String getEffectiveContain(AssociationModel association, String variable)
      throws TechnicalIOException
  {
    String res = null;
    switch (association.getOtherCardinality()) {
      case ONE:
        res = Constants.URL_OBJECT_UTILS + ".nullSafeEquals(" + getAttributeManager().getGetter(getInternalAttributeForAssociation(association)).getName() + "(), " + variable + ")";
        break;
      case MANY:
        if (association.getQualifier() == null) {
          res = getAttributeManager().getGetter(getInternalAttributeForAssociation(association)).getName() + "().contains(" + variable + ")";
        }
        else {
          // TODO insure to get the right getter
          res = Constants.URL_OBJECT_UTILS + ".nullSafeEquals(" + getAttributeManager().getGetter(getInternalAttributeForAssociation(association)).getName() + "().get(" + variable + ".get"
              + getNameManager().getUpperCaseFirstLetterName(association.getQualifier()) + "()), " + variable + ")";
        }
    }
    return res;
  }



  /**
   * returns the command to use to remove an element from an association.
   * 
   * @param association
   *          the association to consider
   * @param variable
   *          the variable containing the element to remove
   * @return the command to use to remove an element from an association
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private String getEffectiveRemove(AssociationModel association, String variable)
      throws TechnicalIOException
  {
    String res = null;
    switch (association.getOtherCardinality()) {
      case ONE:
        res = getAttributeManager().getSetter(getInternalAttributeForAssociation(association)).getName() + "(null)";
        break;
      case MANY:
        if (association.getQualifier() == null) {
          res = getAttributeManager().getGetter(getInternalAttributeForAssociation(association)).getName() + "().remove(" + variable + ")";
        }
        else {
          // TODO insure to get the right getter
          res = getAttributeManager().getGetter(getInternalAttributeForAssociation(association)).getName() + "().remove(" + variable + ".get"
              + getNameManager().getUpperCaseFirstLetterName(association.getQualifier()) + "())";
        }
    }
    return res;
  }



  /**
   * returns all methods related to the a two way association without the
   * priority.
   * 
   * @param association
   *          the association to consider
   * @return all methods related to the association
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private Collection<OperationModel> getNonPriorityAccessorMethodForTwoWayAssociation(AssociationModel association)
      throws TechnicalIOException
  {
    StringBuilder body = null;
    String parameterName = null;
    List<OperationModel> res = new ArrayList<OperationModel>();
    if (association.getOtherCardinality() == AssociationCardinality.MANY) {

      OperationModel addOperation = getAddOperationTemplate(association);
      parameterName = addOperation.getParameterModels().get(0).getName();
      body = new StringBuilder();
      body.append("if(").append(parameterName).append(" == null) {\nreturn;\n}\n");
      body.append("if(").append(getEffectiveContain(association, parameterName)).append(") {\nreturn;\n}\n");
      body.append(parameterName).append(".").append(getOtherAddOperationTemplate(association).getName()).append("(this);\n");
      addOperation.setBody(body.toString());
      res.add(addOperation);

      OperationModel removeOperation = getRemoveOperationTemplate(association);
      parameterName = addOperation.getParameterModels().get(0).getName();
      body = new StringBuilder();
      body.append("if(").append(parameterName).append(" == null) {\nreturn;\n}\n");
      body.append("if(!").append(getEffectiveContain(association, parameterName)).append(") {\nreturn;\n}\n");
      body.append(parameterName).append(".").append(getOtherRemoveOperationTemplate(association).getName()).append("(").append(getOtherRemoveOperationArgument(association)).append(");\n");
      removeOperation.setBody(body.toString());
      res.add(removeOperation);
    }
    else {
      OperationModel setOperation = getAttributeManager().getSetter(getAttributeForAssociation(association));
      parameterName = setOperation.getParameterModels().get(0).getName();
      OperationModel internalAssociationGetter = getAttributeManager().getGetter(getInternalAttributeForAssociation(association));
      OperationModel otherRemoveOperation = getOtherRemoveOperationTemplate(association);
      OperationModel otherAddOperation = getOtherAddOperationTemplate(association);

      body = new StringBuilder();
      body.append("if(").append(Constants.URL_OBJECT_UTILS).append(".nullSafeEquals(").append(internalAssociationGetter.getName()).append("(), ").append(parameterName).append(")) {\nreturn;\n}");
      body.append("if(").append(internalAssociationGetter.getName()).append("() != null) {\n");
      body.append(internalAssociationGetter.getName()).append("().").append(otherRemoveOperation.getName()).append("(").append(getOtherRemoveOperationArgument(association)).append(");\n");
      body.append("}\n");
      body.append("if(").append(parameterName).append(" != null) {\n");
      body.append(parameterName).append(".").append(otherAddOperation.getName()).append("(this);\n");
      body.append("}\n");
      setOperation.setBody(body.toString());
      res.add(setOperation);
    }
    return res;
  }



  /**
   * return an {@link OperationModel} being the template for the add operation.
   * 
   * @param association
   *          the association to consider
   * @return an {@link OperationModel} begin the template for the add operation
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private OperationModel getOtherAddOperationTemplate(AssociationModel association)
      throws TechnicalIOException
  {
    OperationModel res = null;
    switch (association.getCurrentCardinality()) {
      case ONE:
        return getAttributeManager().getSetter(getAttributeForAssociation(association.getOtherEndAssociation()));
      case MANY:
        return getAddOperationTemplate(association.getOtherEndAssociation());
    }
    return res;
  }



  /**
   * returns the argument to give to the remove method of the other end of the
   * association, to disassociate the current element.
   * 
   * @param association
   *          association the association to consider
   * @return the argument to give to the remove method of the other end of the
   *         association, to disassociate the current element
   */
  private String getOtherRemoveOperationArgument(AssociationModel association)
  {
    switch (association.getCurrentCardinality()) {
      case ONE:
        return "null";
      case MANY:
        return "this";
    }
    return null;
  }



  /**
   * return an {@link OperationModel} being the template for the remove
   * operation.
   * 
   * @param association
   *          the association to consider
   * @return an {@link OperationModel} begin the template for the remove
   *         operation
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private OperationModel getOtherRemoveOperationTemplate(AssociationModel association)
      throws TechnicalIOException
  {
    OperationModel res = null;
    switch (association.getCurrentCardinality()) {
      case ONE:
        return getAttributeManager().getSetter(getAttributeForAssociation(association.getOtherEndAssociation()));
      case MANY:
        return getRemoveOperationTemplate(association.getOtherEndAssociation());
    }
    return res;
  }



  /**
   * returns all method relateds to the a two way association with the priority.
   * 
   * @param association
   *          the association to consider
   * @return all methods related to the association
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private Collection<OperationModel> getPriorityAccessorMethodForTwoWayAssociation(AssociationModel association)
      throws TechnicalIOException
  {
    List<OperationModel> res = new ArrayList<OperationModel>();
    String parameterName = null;
    StringBuilder body = null;
    if (association.getOtherCardinality() == AssociationCardinality.MANY) {

      OperationModel addOperation = getAddOperationTemplate(association);
      parameterName = addOperation.getParameterModels().get(0).getName();
      body = new StringBuilder();
      body.append("if(").append(parameterName).append(" == null) { return; }\n");
      body.append("if(").append(getEffectiveContain(association, parameterName)).append(") { return; }\n");
      body.append(parameterName).append(".").append(getEffectiveAdd(association.getOtherEndAssociation(), "this")).append(";\n");
      body.append(getEffectiveAdd(association, parameterName)).append(";\n");
      addOperation.setBody(body.toString());
      res.add(addOperation);

      OperationModel removeOperation = getRemoveOperationTemplate(association);
      parameterName = removeOperation.getParameterModels().get(0).getName();
      body = new StringBuilder();
      body.append("if(").append(parameterName).append(" == null) { return; }\n");
      body.append("if(!").append(getEffectiveContain(association, parameterName)).append(") { return; }\n");
      body.append(parameterName).append(".").append(getEffectiveRemove(association.getOtherEndAssociation(), "this")).append(";\n");
      body.append(getEffectiveRemove(association, parameterName)).append(";\n");
      removeOperation.setBody(body.toString());
      res.add(removeOperation);
    }
    else {
      OperationModel setOperation = getAttributeManager().getSetter(getAttributeForAssociation(association));
      parameterName = setOperation.getParameterModels().get(0).getName();
      OperationModel internalAssociationGetter = getAttributeManager().getGetter(getInternalAttributeForAssociation(association));
      OperationModel internalAssociationSetter = getAttributeManager().getSetter(getInternalAttributeForAssociation(association));
      body = new StringBuilder();
      body.append("if(").append(Constants.URL_OBJECT_UTILS).append(".nullSafeEquals(").append(internalAssociationGetter.getName()).append("(), ").append(parameterName).append(")) {\nreturn;\n}");
      body.append("if(").append(internalAssociationGetter.getName()).append("() != null) {\n");
      body.append(internalAssociationGetter.getName()).append("().").append(getEffectiveRemove(association.getOtherEndAssociation(), "this")).append(";");
      body.append("}\n");
      body.append(internalAssociationSetter.getName()).append("(").append(parameterName).append(");\n");
      body.append("if(").append(parameterName).append(" != null) {\n");
      body.append(parameterName).append(".").append(getEffectiveAdd(association.getOtherEndAssociation(), "this")).append(";");
      body.append("}\n");
      setOperation.setBody(body.toString());
      res.add(setOperation);
    }
    return res;
  }



  /**
   * get a template operation for an Add operation for an association.
   * 
   * @param association
   *          the association to consider
   * @return a template operation for an Add operation for an association
   */
  private OperationModel getRemoveByKeyOperationTemplate(AssociationModel association)
  {
    OperationModel operation = new OperationModelImpl();
    operation.setName("remove" + getNameManager().getUpperCaseFirstLetterName(association) + "ByKey");
    operation.setReturnType(getAttributeManager().getVoidReturnType());

    AttributeModel parameter = new AttributeModelImpl();
    parameter.setName(association.getQualifier().getName());
    parameter.setType(association.getQualifier().getType());
    parameter.setComment("the key of the " + association.getName() + " to remove.");
    parameter.setUmlId(association.getUmlId() + "removeByKeyParameter");
    operation.addParameterModels(parameter);

    operation.setComment("remove a new " + association.getName() + " by its key.");
    operation.setUmlId(association.getUmlId() + "removeByKey");
    return operation;
  }



  /**
   * get a template operation for an Add operation for an association.
   * 
   * @param association
   *          the association to consider
   * @return a template operation for an Add operation for an association
   */
  private OperationModel getRemoveOperationTemplate(AssociationModel association)
  {
    OperationModel operation = new OperationModelImpl();
    operation.setName("remove" + getNameManager().getUpperCaseFirstLetterName(association));
    operation.setReturnType(getAttributeManager().getVoidReturnType());

    AttributeModel parameter = new AttributeModelImpl();
    parameter.setName(association.getName());
    parameter.setType(association.getAssociatedType());
    parameter.setComment("the " + association.getName() + " to remove.");
    parameter.setUmlId(association.getUmlId() + "removeParameter");
    operation.addParameterModels(parameter);

    operation.setComment("remove a " + association.getName() + ".");
    operation.setUmlId(association.getUmlId() + "remove");
    return operation;
  }



  /**
   * get the getter for the association.
   * 
   * @param association
   *          the association to consider
   * @return the getter of the association
   * @throws TechnicalIOException
   *           when an error occurred
   */
  private OperationModel getSimpleGetter(AssociationModel association)
      throws TechnicalIOException
  {
    AttributeModel associationAttribute = getAttributeForAssociation(association);
    OperationModel associationGetter = null;

    OperationModel internalAssociationGetter = getAttributeManager().getGetter(getInternalAttributeForAssociation(association));
    if (association.getQualifier() == null) {
      switch (association.getOtherCardinality()) {
        case ONE:
          associationGetter = getAttributeManager().getGetter(associationAttribute);
          associationGetter.setBody("return " + internalAssociationGetter.getName() + "();");
          break;
        case MANY:
          associationGetter = getAttributeManager().getGetter(getModelDecorator().getPluralObject(associationAttribute));
          associationGetter.setBody("return " + Constants.URL_COLLECTIONS + ".unmodifiableSet(" + internalAssociationGetter.getName() + "());");
          break;
      }
    }
    else {
      associationGetter = getAttributeManager().getGetter(getModelDecorator().getPluralObject(associationAttribute));
      associationGetter.setBody("return " + Constants.URL_COLLECTIONS + ".unmodifiableMap(" + internalAssociationGetter.getName() + "());");
    }
    return associationGetter;
  }

}
